{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "562ddb82",
      "metadata": {},
      "source": [
        "# How to stream full state of your graph\n",
        "\n",
        "LangGraph supports multiple streaming modes. The main ones are:\n",
        "\n",
        "- `values`: This streaming mode streams back values of the graph. This is the\n",
        "  **full state of the graph** after each node is called.\n",
        "- `updates`: This streaming mode streams back updates to the graph. This is the\n",
        "  **update to the state of the graph** after each node is called.\n",
        "\n",
        "This guide covers `streamMode=\"values\"`."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "id": "8e76833b",
      "metadata": {
        "lines_to_next_cell": 2
      },
      "outputs": [],
      "source": [
        "// process.env.OPENAI_API_KEY = \"sk-...\";"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ab95dc97",
      "metadata": {},
      "source": [
        "## Define the state\n",
        "\n",
        "The state is the interface for all of the nodes in our graph.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "1648124b",
      "metadata": {
        "lines_to_next_cell": 2
      },
      "outputs": [],
      "source": [
        "import { Annotation } from \"@langchain/langgraph\";\n",
        "import { BaseMessage } from \"@langchain/core/messages\";\n",
        "\n",
        "const StateAnnotation = Annotation.Root({\n",
        "  messages: Annotation<BaseMessage[]>({\n",
        "    reducer: (x, y) => x.concat(y),\n",
        "  }),\n",
        "});"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "da50fbd8",
      "metadata": {},
      "source": [
        "## Set up the tools\n",
        "\n",
        "We will first define the tools we want to use. For this simple example, we will\n",
        "use create a placeholder search engine. However, it is really easy to create\n",
        "your own tools - see documentation\n",
        "[here](https://js.langchain.com/docs/how_to/custom_tools) on how to do\n",
        "that.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "a8f1ae1c",
      "metadata": {
        "lines_to_next_cell": 2
      },
      "outputs": [],
      "source": [
        "import { tool } from \"@langchain/core/tools\";\n",
        "import { z } from \"zod\";\n",
        "\n",
        "const searchTool = tool(async ({ query: _query }: { query: string }) => {\n",
        "  // This is a placeholder for the actual implementation\n",
        "  return \"Cold, with a low of 3℃\";\n",
        "}, {\n",
        "  name: \"search\",\n",
        "  description:\n",
        "    \"Use to surf the web, fetch current information, check the weather, and retrieve other information.\",\n",
        "  schema: z.object({\n",
        "    query: z.string().describe(\"The query to use in your search.\"),\n",
        "  }),\n",
        "});\n",
        "\n",
        "await searchTool.invoke({ query: \"What's the weather like?\" });\n",
        "\n",
        "const tools = [searchTool];"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "19b27cb3",
      "metadata": {},
      "source": [
        "We can now wrap these tools in a simple\n",
        "[ToolNode](/langgraphjs/reference/classes/langgraph_prebuilt.ToolNode.html).\n",
        "This object will actually run the tools (functions) whenever they are invoked by\n",
        "our LLM.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "id": "f02278b1",
      "metadata": {
        "lines_to_next_cell": 2
      },
      "outputs": [],
      "source": [
        "import { ToolNode } from \"@langchain/langgraph/prebuilt\";\n",
        "\n",
        "const toolNode = new ToolNode(tools);"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "dd55ee5a",
      "metadata": {},
      "source": [
        "## Set up the model\n",
        "\n",
        "Now we will load the\n",
        "[chat model](https://js.langchain.com/docs/concepts/chat_models/).\n",
        "\n",
        "1. It should work with messages. We will represent all agent state in the form\n",
        "   of messages, so it needs to be able to work well with them.\n",
        "2. It should work with\n",
        "   [tool calling](https://js.langchain.com/docs/how_to/tool_calling/#passing-tools-to-llms),\n",
        "   meaning it can return function arguments in its response.\n",
        "\n",
        "<div class=\"admonition tip\">\n",
        "    <p class=\"admonition-title\">Note</p>\n",
        "    <p>\n",
        "        These model requirements are not general requirements for using LangGraph - they are just requirements for this one example.\n",
        "    </p>\n",
        "</div>"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "id": "9c7210e7",
      "metadata": {
        "lines_to_next_cell": 2
      },
      "outputs": [],
      "source": [
        "import { ChatOpenAI } from \"@langchain/openai\";\n",
        "\n",
        "const model = new ChatOpenAI({ model: \"gpt-4o\" });"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "73e59248",
      "metadata": {},
      "source": [
        "After we've done this, we should make sure the model knows that it has these\n",
        "tools available to call. We can do this by calling\n",
        "[bindTools](https://v01.api.js.langchain.com/classes/langchain_core_language_models_chat_models.BaseChatModel.html#bindTools)."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "id": "b4ff23ee",
      "metadata": {
        "lines_to_next_cell": 2
      },
      "outputs": [],
      "source": [
        "const boundModel = model.bindTools(tools);"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "dbe67356",
      "metadata": {},
      "source": [
        "## Define the graph\n",
        "\n",
        "We can now put it all together."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "id": "0ba603bb",
      "metadata": {},
      "outputs": [],
      "source": [
        "import { END, START, StateGraph } from \"@langchain/langgraph\";\n",
        "import { AIMessage } from \"@langchain/core/messages\";\n",
        "\n",
        "const routeMessage = (state: typeof StateAnnotation.State) => {\n",
        "  const { messages } = state;\n",
        "  const lastMessage = messages[messages.length - 1] as AIMessage;\n",
        "  // If no tools are called, we can finish (respond to the user)\n",
        "  if (!lastMessage?.tool_calls?.length) {\n",
        "    return END;\n",
        "  }\n",
        "  // Otherwise if there is, we continue and call the tools\n",
        "  return \"tools\";\n",
        "};\n",
        "\n",
        "const callModel = async (\n",
        "  state: typeof StateAnnotation.State,\n",
        ") => {\n",
        "  // For versions of @langchain/core < 0.2.3, you must call `.stream()`\n",
        "  // and aggregate the message from chunks instead of calling `.invoke()`.\n",
        "  const { messages } = state;\n",
        "  const responseMessage = await boundModel.invoke(messages);\n",
        "  return { messages: [responseMessage] };\n",
        "};\n",
        "\n",
        "const workflow = new StateGraph(StateAnnotation)\n",
        "  .addNode(\"agent\", callModel)\n",
        "  .addNode(\"tools\", toolNode)\n",
        "  .addEdge(START, \"agent\")\n",
        "  .addConditionalEdges(\"agent\", routeMessage)\n",
        "  .addEdge(\"tools\", \"agent\");\n",
        "\n",
        "const graph = workflow.compile();"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a1ab3ad3",
      "metadata": {},
      "source": [
        "## Stream values\n",
        "\n",
        "We can now interact with the agent. Between interactions you can get and update\n",
        "state.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "id": "cbcf7c39",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "[ [ 'user', \"what's the weather in sf\" ] ]\n",
            "\n",
            "====\n",
            "\n",
            "[\n",
            "  [ 'user', \"what's the weather in sf\" ],\n",
            "  AIMessage {\n",
            "    \"id\": \"chatcmpl-9y660d49eLzT7DZeBk2ZmX8C5f0LU\",\n",
            "    \"content\": \"\",\n",
            "    \"additional_kwargs\": {\n",
            "      \"tool_calls\": [\n",
            "        {\n",
            "          \"id\": \"call_iD5Wk4vPsTckffDKJpEQaMkg\",\n",
            "          \"type\": \"function\",\n",
            "          \"function\": \"[Object]\"\n",
            "        }\n",
            "      ]\n",
            "    },\n",
            "    \"response_metadata\": {\n",
            "      \"tokenUsage\": {\n",
            "        \"completionTokens\": 17,\n",
            "        \"promptTokens\": 70,\n",
            "        \"totalTokens\": 87\n",
            "      },\n",
            "      \"finish_reason\": \"tool_calls\",\n",
            "      \"system_fingerprint\": \"fp_3aa7262c27\"\n",
            "    },\n",
            "    \"tool_calls\": [\n",
            "      {\n",
            "        \"name\": \"search\",\n",
            "        \"args\": {\n",
            "          \"query\": \"current weather in San Francisco\"\n",
            "        },\n",
            "        \"type\": \"tool_call\",\n",
            "        \"id\": \"call_iD5Wk4vPsTckffDKJpEQaMkg\"\n",
            "      }\n",
            "    ],\n",
            "    \"invalid_tool_calls\": [],\n",
            "    \"usage_metadata\": {\n",
            "      \"input_tokens\": 70,\n",
            "      \"output_tokens\": 17,\n",
            "      \"total_tokens\": 87\n",
            "    }\n",
            "  }\n",
            "]\n",
            "\n",
            "====\n",
            "\n",
            "[\n",
            "  [ 'user', \"what's the weather in sf\" ],\n",
            "  AIMessage {\n",
            "    \"id\": \"chatcmpl-9y660d49eLzT7DZeBk2ZmX8C5f0LU\",\n",
            "    \"content\": \"\",\n",
            "    \"additional_kwargs\": {\n",
            "      \"tool_calls\": [\n",
            "        {\n",
            "          \"id\": \"call_iD5Wk4vPsTckffDKJpEQaMkg\",\n",
            "          \"type\": \"function\",\n",
            "          \"function\": \"[Object]\"\n",
            "        }\n",
            "      ]\n",
            "    },\n",
            "    \"response_metadata\": {\n",
            "      \"tokenUsage\": {\n",
            "        \"completionTokens\": 17,\n",
            "        \"promptTokens\": 70,\n",
            "        \"totalTokens\": 87\n",
            "      },\n",
            "      \"finish_reason\": \"tool_calls\",\n",
            "      \"system_fingerprint\": \"fp_3aa7262c27\"\n",
            "    },\n",
            "    \"tool_calls\": [\n",
            "      {\n",
            "        \"name\": \"search\",\n",
            "        \"args\": {\n",
            "          \"query\": \"current weather in San Francisco\"\n",
            "        },\n",
            "        \"type\": \"tool_call\",\n",
            "        \"id\": \"call_iD5Wk4vPsTckffDKJpEQaMkg\"\n",
            "      }\n",
            "    ],\n",
            "    \"invalid_tool_calls\": [],\n",
            "    \"usage_metadata\": {\n",
            "      \"input_tokens\": 70,\n",
            "      \"output_tokens\": 17,\n",
            "      \"total_tokens\": 87\n",
            "    }\n",
            "  },\n",
            "  ToolMessage {\n",
            "    \"content\": \"Cold, with a low of 3℃\",\n",
            "    \"name\": \"search\",\n",
            "    \"additional_kwargs\": {},\n",
            "    \"response_metadata\": {},\n",
            "    \"tool_call_id\": \"call_iD5Wk4vPsTckffDKJpEQaMkg\"\n",
            "  }\n",
            "]\n",
            "\n",
            "====\n",
            "\n",
            "[\n",
            "  [ 'user', \"what's the weather in sf\" ],\n",
            "  AIMessage {\n",
            "    \"id\": \"chatcmpl-9y660d49eLzT7DZeBk2ZmX8C5f0LU\",\n",
            "    \"content\": \"\",\n",
            "    \"additional_kwargs\": {\n",
            "      \"tool_calls\": [\n",
            "        {\n",
            "          \"id\": \"call_iD5Wk4vPsTckffDKJpEQaMkg\",\n",
            "          \"type\": \"function\",\n",
            "          \"function\": \"[Object]\"\n",
            "        }\n",
            "      ]\n",
            "    },\n",
            "    \"response_metadata\": {\n",
            "      \"tokenUsage\": {\n",
            "        \"completionTokens\": 17,\n",
            "        \"promptTokens\": 70,\n",
            "        \"totalTokens\": 87\n",
            "      },\n",
            "      \"finish_reason\": \"tool_calls\",\n",
            "      \"system_fingerprint\": \"fp_3aa7262c27\"\n",
            "    },\n",
            "    \"tool_calls\": [\n",
            "      {\n",
            "        \"name\": \"search\",\n",
            "        \"args\": {\n",
            "          \"query\": \"current weather in San Francisco\"\n",
            "        },\n",
            "        \"type\": \"tool_call\",\n",
            "        \"id\": \"call_iD5Wk4vPsTckffDKJpEQaMkg\"\n",
            "      }\n",
            "    ],\n",
            "    \"invalid_tool_calls\": [],\n",
            "    \"usage_metadata\": {\n",
            "      \"input_tokens\": 70,\n",
            "      \"output_tokens\": 17,\n",
            "      \"total_tokens\": 87\n",
            "    }\n",
            "  },\n",
            "  ToolMessage {\n",
            "    \"content\": \"Cold, with a low of 3℃\",\n",
            "    \"name\": \"search\",\n",
            "    \"additional_kwargs\": {},\n",
            "    \"response_metadata\": {},\n",
            "    \"tool_call_id\": \"call_iD5Wk4vPsTckffDKJpEQaMkg\"\n",
            "  },\n",
            "  AIMessage {\n",
            "    \"id\": \"chatcmpl-9y660ZKNXvziVJze0X5aTlZ5IoN35\",\n",
            "    \"content\": \"Currently, in San Francisco, it's cold with a temperature of around 3℃ (37.4°F).\",\n",
            "    \"additional_kwargs\": {},\n",
            "    \"response_metadata\": {\n",
            "      \"tokenUsage\": {\n",
            "        \"completionTokens\": 23,\n",
            "        \"promptTokens\": 103,\n",
            "        \"totalTokens\": 126\n",
            "      },\n",
            "      \"finish_reason\": \"stop\",\n",
            "      \"system_fingerprint\": \"fp_3aa7262c27\"\n",
            "    },\n",
            "    \"tool_calls\": [],\n",
            "    \"invalid_tool_calls\": [],\n",
            "    \"usage_metadata\": {\n",
            "      \"input_tokens\": 103,\n",
            "      \"output_tokens\": 23,\n",
            "      \"total_tokens\": 126\n",
            "    }\n",
            "  }\n",
            "]\n",
            "\n",
            "====\n",
            "\n"
          ]
        }
      ],
      "source": [
        "let inputs = { messages: [{ role: \"user\", content: \"what's the weather in sf\" }] };\n",
        "\n",
        "for await (\n",
        "  const chunk of await graph.stream(inputs, {\n",
        "    streamMode: \"values\",\n",
        "  })\n",
        ") {\n",
        "  console.log(chunk[\"messages\"]);\n",
        "  console.log(\"\\n====\\n\");\n",
        "}"
      ]
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "TypeScript",
      "language": "typescript",
      "name": "tslab"
    },
    "language_info": {
      "codemirror_mode": {
        "mode": "typescript",
        "name": "javascript",
        "typescript": true
      },
      "file_extension": ".ts",
      "mimetype": "text/typescript",
      "name": "typescript",
      "version": "3.7.2"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 5
}
